﻿using System;
using System.ComponentModel;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
using Microsoft.Surface.Presentation;
using Microsoft.Surface.Presentation.Controls;
using Microsoft.Surface.Presentation.Manipulations;

namespace Black.SurfaceToolkit.Controls
{
    /// <summary>
    /// Contrôle pour le glisser-dépôser du <see cref="T:Black.SurfaceToolkit.Controls.CanvasScatterView" />.
    /// </summary>
    [Browsable(false)]
    public sealed class CanvasScatterViewDragItem : SurfaceControl
    {
        #region Routed Evénements

        /// <summary>
        /// Identifie l'événement routé <see cref="E:Black.SurfaceToolkit.Controls.CanvasScatterViewDragItem.ItemDragEnded" />.
        /// </summary>
        internal static readonly RoutedEvent ItemDragEndedEvent = EventManager.RegisterRoutedEvent("ItemDragEnded", RoutingStrategy.Bubble, typeof(DragItemRoutedEventHandler), typeof(CanvasScatterViewDragItem));

        #endregion

        #region Evénéments

        /// <summary>
        /// Se produit lors du dépôt.
        /// </summary>
        internal event DragItemRoutedEventHandler ItemDragEnded
        {
            add
            {
                this.AddHandler(CanvasScatterViewDragItem.ItemDragEndedEvent, value);
            }
            remove
            {
                this.RemoveHandler(CanvasScatterViewDragItem.ItemDragEndedEvent, value);
            }
        }

        #endregion

        #region Accesseurs

        /// <summary>
        /// Obtient ou définit les informations de glisser-déposser.
        /// </summary>
        [Browsable(false)]
        public object DragData
        {
            get;
            private set;
        }

        #endregion

        #region Variables d'instance

        private int m_iContactId;
        private Canvas m_oContainer;
        private ImageSource m_oImage;
        private Image m_oRefImage;
        private Affine2DManipulationProcessor m_oTracker;

        #endregion

        #region Constructeurs

        /// <summary>
        /// Initialise la classe <see cref="T:Black.SurfaceToolkit.Controls.CanvasScatterViewDragItem" />.
        /// </summary>
        static CanvasScatterViewDragItem()
        {
            FrameworkElement.DefaultStyleKeyProperty.OverrideMetadata(typeof(CanvasScatterViewDragItem), new FrameworkPropertyMetadata(typeof(CanvasScatterViewDragItem)));
        }

        /// <summary>
        /// Initialise une nouvelle instance de <see cref="T:Black.SurfaceToolkit.Controls.CanvasScatterViewDragItem" />.
        /// </summary>
        /// <param name="p_oContainer">Conteneur du glisser-déposer.</param>
        /// <param name="p_oData">Donnée du glisser-déposer.</param>
        /// <param name="p_oContact">Contact du glisser-déposer.</param>
        /// <param name="p_oImage">Image du glisser-déposer.</param>
        /// <param name="p_dWitdh">Largeur désiré pour le contrôle.</param>
        /// <param name="p_dHeight">Hauteur désiré pour le contrôle.</param>
        internal CanvasScatterViewDragItem(Canvas p_oContainer, object p_oData, Contact p_oContact, ImageSource p_oImage, double p_dWitdh, double p_dHeight)
        {
            // Initialisation custom

            this.m_iContactId = p_oContact.Id;
            this.DragData = p_oData;
            this.m_oContainer = p_oContainer;
            this.m_oImage = p_oImage;

            this.m_oTracker = new Affine2DManipulationProcessor(Affine2DManipulations.TranslateX | Affine2DManipulations.TranslateY, p_oContainer);
            this.m_oTracker.Affine2DManipulationCompleted += new EventHandler<Affine2DOperationCompletedEventArgs>(this.Tracker_Affine2DManipulationCompleted);
            this.m_oTracker.Affine2DManipulationDelta += new EventHandler<Affine2DOperationDeltaEventArgs>(this.Tracker_Affine2DManipulationDelta);

            // Ajout au container

            this.m_oContainer.Children.Add(this);

            // Moidification taille

            this.Width = p_dWitdh;
            this.Height = p_dHeight;

            // On se place

            Point oPosition = p_oContact.GetPosition(this.m_oContainer);

            Canvas.SetLeft(this, oPosition.X - (this.Width / 2));
            Canvas.SetTop(this, oPosition.Y - (this.Height / 2));

            // On démare le tracking

            this.m_oTracker.BeginTrack(p_oContact);

            // On se branche sur le lancement

            this.Loaded += new RoutedEventHandler(this.CanvasScatterViewDragItem_Loaded);
        }

        #endregion

        #region Evénements XAML

        /// <summary>
        /// Appelée lorsque le contrôle est chargé.
        /// </summary>
        /// <param name="p_oSender">Source de l'événement.</param>
        /// <param name="p_oArgs">Données de l'événement.</param>
        private void CanvasScatterViewDragItem_Loaded(object p_oSender, RoutedEventArgs p_oArgs)
        {
            // Récupération image

            this.m_oRefImage = (Image)this.Template.FindName("PART_imgMain", this);

            // Affectation image

            this.m_oRefImage.Source = this.m_oImage;
        }

        /// <summary>
        /// Appelée lorsque la manipulation est terminée.
        /// </summary>
        /// <param name="p_oSender">Source de l'événement.</param>
        /// <param name="p_oArgs">Données de l'événement.</param>
        private void Tracker_Affine2DManipulationCompleted(object p_oSender, Affine2DOperationCompletedEventArgs p_oArgs)
        {
            // On retire l'élément du Canvas

            this.m_oContainer.Children.Remove(this);

            // On lance l'event

            this.RaiseItemDragEndedEvent(this.DragData, p_oArgs.ManipulationOrigin, this.m_iContactId);
        }

        /// <summary>
        /// Appelée lorsque la manipulation produit un changement.
        /// </summary>
        /// <param name="p_oSender">Source de l'événement.</param>
        /// <param name="p_oArgs">Données de l'événement.</param>
        private void Tracker_Affine2DManipulationDelta(object p_oSender, Affine2DOperationDeltaEventArgs p_oArgs)
        {
            Canvas.SetLeft(this, Canvas.GetLeft(this) + p_oArgs.Delta.X);
            Canvas.SetTop(this, Canvas.GetTop(this) + p_oArgs.Delta.Y);
        }

        #endregion

        #region Fonctions privées

        /// <summary>
        /// Déclenche l'événément <see cref="E:Black.SurfaceToolkit.Controls.CanvasScatterViewDragItem.ItemDragEnded" />.
        /// </summary>
        /// <param name="p_oData">Informations de glisser-déposser.</param>
        /// <param name="p_oPosition">Position du dépôt.</param>
        /// <param name="p_iContactId">Identifiant du contact.</param>
        private void RaiseItemDragEndedEvent(object p_oData, Point p_oPosition, int p_iContactId)
        {
            this.RaiseEvent(new DragItemRoutedEventArgs(CanvasScatterViewDragItem.ItemDragEndedEvent, p_oData, p_oPosition, p_iContactId));
        }

        #endregion
    }
}